package parser;

import java.io.PrintWriter;

import lexer.TokenType;

/**
 * Parses and stores a LOGO procedure. Grammar: PROC NAME(ARGUMENT) (statements)
 */
public class LogoProcedure extends LogoIdentifier {

	LogoIdentifier argumentIdentifier;
	LogoStatements procedureBody;

	/**
	 * Reads in a new Procedure from the iterator. Adds its self to the
	 * LogoScope since all procedures are visible from all parts of a
	 * LogoProgram
	 * 
	 * @param LogoToken
	 * @param LogoScope
	 * @throws LogoParseSyntaxException
	 * @throws LogoParseException
	 */
	LogoProcedure(TokenIterator it, LogoScope scope) throws LogoParseSyntaxException, LogoParseException {
		super(LogoIdentifierType.PROCEDURE);
		parseTokens(it, scope);
	}

	private void parseTokens(TokenIterator it, LogoScope scope) throws LogoParseException {
		it.ExpectedNext(TokenType.BEGIN_PROC);

		String procedureIdent = it.ExpectedNext(TokenType.IDENTIFIER).getContents();

		this.setIdentifier(procedureIdent);

		try {
			it.ExpectedNext(TokenType.LEFT_BRACKET);

			String argumentIdentStr = it.ExpectedNext(TokenType.IDENTIFIER).getContents();
			argumentIdentifier = new LogoVariable(argumentIdentStr);

			it.ExpectedNext(TokenType.RIGHT_BRACKET);

			procedureBody = new LogoStatements(it, TokenType.BEGIN_PROC, false);

			scope.pushIdentifier(this);
		} catch (LogoParseException e) {
			throw new LogoParseException(e.getMessage() + " in procedure " + procedureIdent);
		}
	}

	@Override
	public void resolveIdentifiers(LogoScope scope) throws LogoParseException {

		// We only want the argument variable to be in scope for this body.
		// So we push it at the start then Pop it after checking
		scope.pushIdentifier(argumentIdentifier);
		try {
			procedureBody.resolveIdentifiers(scope);
		}
		catch(LogoParseException e) {
			throw new LogoParseException(e.getMessage() + "\n in procedure " + this.identifier);
		}
		
		scope.popIdentifier(argumentIdentifier);
	}

	/**
	 * Example output: /MAIN { 11 LDRAGON } def
	 */
	@Override
	public void generatePostScriptCode(PrintWriter stream) {
		stream.println("/" + this.identifier + " {");
		
		if (!this.argumentIdentifier.identifier.equals("VOID")) {
			
			// We tried using dictionaries (http://www.ugrad.math.ubc.ca/Flat/lang.html) however
			// They didn't work when a function called its self
			stream.println("Arg exch");
			stream.println("/Arg exch def");
		}

		procedureBody.generatePostScriptCode(stream);
		stream.println("} def \n");
	}

}
